#include <stdlib.h>

#include <direct/types.h>
#include <direct/debug.h>

#include "dtk_list.h"
#include "dtk_debug.h"

void dtk_list_init(DList* list, DPointer data)
{
    DTK_ASSERT(list != NULL);

    list->self = data;
    return;
}

void dtk_link_init(DLink* link, DPointer data)
{
    DTK_ASSERT(link != NULL);

    link->data = data;
    return;
}

DBoolean dtk_list_contains_element(DList *list, DLink *link )
{
    DLink* first = list->data;
    DLink* temp = first;

    if (first == NULL)
    {
        return DTK_FALSE;
    }
   
    do
    {
        if (temp == link)
            return DTK_TRUE;

        temp = temp->next;
        
    }while (temp != first);

    return DTK_FALSE;
}

void dtk_list_prepend( DList *list, DLink *link )
{
    DLink *first = list->data;

    if (DTK_TRUE == dtk_list_contains_element (list, link))
    {
        return;
    }

    if (first)
    {
        D_MAGIC_ASSERT( first, DLink );

        link->next = first;
        link->prev = first->prev;
        first->prev = link;
        link->prev->next = link;
    }
    else
        link->next = link->prev = link;
    list->data = link;
    list->count ++;

    D_MAGIC_SET( link, DLink );
    return;
}

void dtk_list_append( DList *list, DLink *link )
{
    DLink *first = list->data;

    if (DTK_TRUE == dtk_list_contains_element (list, link))
    {
        return;
    }
    
    if (first) {
        DLink* last = first->prev;
          
        D_MAGIC_ASSERT( first, DLink );
        D_MAGIC_ASSERT( last, DLink );

        link->next = first;
        link->prev = last;
        last->next = link;
        first->prev = link;
    }
    else
        list->data = link->prev = link->next = link;

    list->count ++;

    D_MAGIC_SET( link, DLink );
}

DInt dtk_list_get_count (DList* list)
{
    return list->count;
}

void dtk_list_remove( DList *list, DLink *link )
{
    DLink *next;
    DLink *prev;

    D_ASSERT( list != NULL );
     
    D_MAGIC_ASSERT( list->data, DLink );
    D_MAGIC_ASSERT( link, DLink );

    if (DTK_TRUE != dtk_list_contains_element (list, link))
    {
       return;
    }

    next = link->next;
    prev = link->prev;

    if (next != link)
    {
       next->prev = prev;
       prev->next = next;
       if (list->data == link)
       {
           list->data = next;
       }
    }

    link->next = link->prev = NULL;
    list->count --;

    if (list->count == 0)
    {
        list->data = NULL;
    }
 
    D_MAGIC_CLEAR( link );
}

DLink* dtk_list_get_next (DLink* link)
{
    D_MAGIC_ASSERT(link, DLink);

    return link->next;
}

DLink* dtk_list_get_prev (DLink* link)
{
    D_MAGIC_ASSERT(link, DLink);

    return link->prev;
}

DLink* dtk_list_get_first (DList* list)
{
    D_ASSERT(list != NULL);

    return list->data;
}
DBoolean dtk_list_check_link(DLink *link )
{
     D_MAGIC_ASSERT_IF( link, DLink );

     return link != NULL;
}

DBoolean dtk_list_insert_by_order (DList* list, DLink* link, DTKCompare compare)
{
    DInt count;
    DInt i;
    DLink* sub_link;
    DInt ret;
    if (list == NULL || link == NULL || compare == NULL)
    {
        return DTK_FALSE;
    }
    

    count = dtk_list_get_count(list);
    if (count == 0)
    {
        dtk_list_append(list, link);
        return DTK_TRUE;
    }

    if (DTK_TRUE == dtk_list_contains_element (list, link))
    {
        return;
    }

    sub_link = dtk_list_get_first(list);
    for (i = 0; (i < count && sub_link); i++)
    {
        ret = compare (link, sub_link);
        if (ret >= 0)
        {
            link->prev = sub_link->prev;
            link->next = sub_link;
            sub_link->prev = link;
            link->prev->next = link;
            list->count ++;
            if (list->data == sub_link)
            {
                list->data = link;
            }
            D_MAGIC_SET( link, DLink );
            return DTK_TRUE;
        }
        sub_link = dtk_list_get_next(sub_link);
    }

    if (sub_link)
    {
        link->next = sub_link->next;
        link->prev = sub_link;
        sub_link->next->prev = link;
        sub_link->next = link;
        
        list->count ++;
        D_MAGIC_SET( link, DLink );
        return DTK_TRUE;
    }

    return DTK_FALSE;
}

void dtk_list_insert_before (DList* list, DLink* next, DLink* link)
{
    DLink* temp;
    if (list == NULL || link == NULL)
    {
        return;
    }

    if (next == NULL || next == list->data)
    {
        dtk_list_prepend(list, link);
    }
    else
    {
        link->prev = next->prev;
        link->next = next;
        next->prev = link;        
        D_MAGIC_SET( link, DLink );
        list->count ++;
    }

    return;
}

DBoolean dtk_list_foreach(DList* list, LinkCallBack cb, ...)
{
    DInt count, i;
    DLink *link, *next;
    DBoolean ret;
    va_list args;   

    DTK_ASSERT(cb != NULL);

    if (list->count <= 0)
    {
        return DTK_FALSE;
    }

    link = list->data;
    
    for (i = 0; i < list->count; i ++)
    {
        next = link->next;
        va_start (args, cb);
        ret = cb(list, link, args);
        va_end (args);        
        if (ret == DTK_TRUE)
        {
            return DTK_TRUE;
        }
        link = next;
    }
    
    return DTK_FALSE;
}

